/**
 * LinkedLST
 * May 13, 2011
 * BarcodeView.java - Barcode scanning activity (for lookup)
 */

package edu.washington.cs.labspecimentransport;

import java.util.ArrayList;

import org.apache.http.conn.ConnectTimeoutException;

import zxing.contrib.PlanarYUVLuminanceSource;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnKeyListener;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.hardware.Camera;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Vibrator;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.google.zxing.BinaryBitmap;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;

import edu.washington.cs.labspecimentransport.data.DataQuery;
import edu.washington.cs.labspecimentransport.dataobjects.Item;
import edu.washington.cs.labspecimentransport.dataobjects.ItemType;
import edu.washington.cs.labspecimentransport.ops.CommonHandlers;
import edu.washington.cs.labspecimentransport.ops.Keys;
import edu.washington.cs.labspecimentransport.ops.ScannerManager;

/**
 * A BarcodeView displays a scanning view through the camera. Each successful scan is
 * followed by displaying on the screen the results of processing the scan 
 * (not in scan list, already scanned, or scan successful and display scanned barcode).	
 */
public class BarcodeLookupView extends Activity {

	// variables for zxing operations
    private SurfaceView previewView; 	// where to draw the camera preview
    private ScannerManager manager; 	// manages scanning functionality
    private ImageView greyscale; 		// where to draw greyscale scan preview
    private View scanArea; 				// the scanning area
    private TextView txtScanResult; 	// textfield where scan results will be displayed
    
    // preferred dimensions for the camera preview frame
    private int width = 480; 
    private int height = 320; 
    
    // inner offsets of the scan area from the camera preview frame
    private int dstLeft, dstTop, dstWidth, dstHeight;
    
    private String lastScanned; // last scanned barcode
    private Integer scannedId;	// item id of the last scanned barcode

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
        // hide the window title.
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        
        // set the content view
		setContentView(R.layout.barcode_lookup);

		// lock the screen in landscape orientation
		setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
		getViews(); 
	}
	
	/** 
	 * Get various views needed for scanning 
	 */
	private void getViews() {
        greyscale = (ImageView) this.findViewById (R.id.greyscalePreview);
        scanArea = (View) this.findViewById (R.id.scanArea);
        previewView = (SurfaceView) this.findViewById (R.id.cam_preview);
        manager = new ScannerManager (previewView.getHolder(), width, height, 
                previewCallback); 
        txtScanResult = (TextView) this.findViewById (R.id.barcode_last_scanned);
	}
	
	/**
     * Define the options menu.
     */
    public boolean onCreateOptionsMenu(Menu menu) {
    	menu.add(0, Keys.MENUID_ENTER_BARCODE, 0, R.string.option_enter_barcode);
    	menu.add(0, Keys.MENUID_HOME, 0, R.string.option_home);
    	menu.add(0, Keys.MENUID_HELP, 0, R.string.option_help);
    	return true;
    }

    /**
     * Define functionality for each item in options menu.
     */
    public boolean onOptionsItemSelected(MenuItem item) {   
        switch(item.getItemId()) {
        case Keys.MENUID_ENTER_BARCODE:
        	manager.stopScanning();
        	
        	// inflate a view for the dialog to use
            LayoutInflater factory = LayoutInflater.from(this);
            View view = factory.inflate(R.layout.dialog_textfield, null);
            
            // set properties for the dialog
            AlertDialog.Builder build = new AlertDialog.Builder(BarcodeLookupView.this);
            build.setTitle(R.string.barcode_dialog_barcodeprompt);
            build.setView(view);

            // add a "Submit" button and define its click handler
            build.setPositiveButton(getString(R.string.dialog_submit), 
            	new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton) 
                    {
                    	manager.stopScanning();
                    	EditText textField = (EditText) 
                    		((AlertDialog)dialog).findViewById(R.id.text_field);
                    	dialog.dismiss();
                    	handleScanResult(textField.getText().toString(), 
                    			getString(R.string.barcode_ui_manualSuccess) 
                    			+ " " + textField.getText());
                    }
                });
            // add a "Cancel" button and define its click handler
            build.setNegativeButton(getString(R.string.dialog_cancel), 
            	new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton) {
                    	manager.startScanning();
                    }
                }
            );
            
            // create the dialog
        	AlertDialog dialog = build.create();
        	dialog.setOnKeyListener(new OnKeyListener() {
				public boolean onKey(DialogInterface dialogInt, int keycode,
						KeyEvent event) {
					if (keycode == KeyEvent.KEYCODE_BACK) {
						dialogInt.dismiss();
						manager.startScanning();
						return true;
					}
					return false;
				}
        		
        	});
        	
			// finally, show the dialog
            dialog.show();
        	break;
        	
        case Keys.MENUID_HOME:
        	CommonHandlers.returnHome(this);
        	break;
        	
        case Keys.MENUID_HELP:
        	CommonHandlers.startHelp(this);
        	break;
        	
        default:
        	super.onOptionsItemSelected(item);
        	break;
        }
        
        return true;
    }
	
    /**
     * Start the camera's timer task on resume.
     */
    protected void onResume() {
    	super.onResume();
    	setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    	manager.startScanning();
    }
    
	/**
	 * Stop the camera's timer task on pause.
	 */
	protected void onPause() {
		manager.stopScanning();
		super.onPause();
	}
	
    /** 
     * Callback to process preview frame 
     */ 
    private Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() { 
        public void onPreviewFrame (byte [] data, Camera camera) { 
        	// calculate the dimensions of the scanning area
            if (dstLeft == 0) {// only one assignment 
                dstLeft = scanArea.getLeft() * width
                		/ getWindowManager().getDefaultDisplay().getWidth();  
                dstTop = scanArea.getTop() * height 
                        / getWindowManager().getDefaultDisplay().getHeight(); 
                dstWidth = (scanArea.getRight() - scanArea.getLeft()) * width 
                        / getWindowManager().getDefaultDisplay().getWidth(); 
                dstHeight = (scanArea.getBottom() - scanArea.getTop()) * height 
                        / getWindowManager().getDefaultDisplay().getHeight(); 
            }
  
            // get the grayscale 
            PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource ( 
                    data, width, height, 
                    dstLeft, dstTop,
                    dstWidth, dstHeight);
            Bitmap mBitmap = source.renderCroppedGreyscaleBitmap();
            
            // display grayscale 
            greyscale.setImageBitmap (mBitmap); 
            BinaryBitmap bitmap = new BinaryBitmap (new HybridBinarizer (source)); 
            MultiFormatReader reader = new MultiFormatReader(); 
            try {
                // try to decode the image using greyscale
            	Result result = reader.decode(bitmap); 

                ToneGenerator tone=new ToneGenerator(AudioManager.STREAM_ALARM, 50);
                tone.startTone(ToneGenerator.TONE_CDMA_ABBR_ALERT);
                
                handleScanResult(result.getText(), 
                		getString(R.string.barcode_ui_scanSuccess) + " " + result.getText()); 
            } catch (NotFoundException e) { 
                // do nothing if greyscale did not contain a recognizable barcode
            } 
        } 
    }; 
    
    /**
     * Handler for a successful scan.
     * @param scannedBarcode the barcode that was scanned
     * @param msg the message to display to the user on success
     */
    private void handleScanResult(String scannedBarcode, String msg) {
    	Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
    	v.vibrate(300);
    	lastScanned = scannedBarcode;
    	txtScanResult.setText (msg);
    	new LookupItem().execute();
    }
    
    /**
     * Service to look up information for an item.
     */
	private class LookupItem extends AsyncTask<Void, Void, Void> {
		ProgressDialog progressDialog;
		boolean success;
		boolean timeout;
		Intent i;

		@Override
		protected void onPreExecute() {
			super.onPreExecute();
    		// stop scanning until dialog is dismissed
    		manager.stopScanning();
    		timeout = false;
			progressDialog = ProgressDialog.show(BarcodeLookupView.this, "", 
					getString(R.string.barcode_load_checkBarcode), true, true,
					new DialogInterface.OnCancelListener(){
				public void onCancel(DialogInterface dialog) {
					LookupItem.this.cancel(true);
					finish();
				}
			});
		}		
		
		@Override
		protected Void doInBackground(Void... params) {
			try {
				DataQuery query = DataQuery.getInstance();
				
				// get the item id of the scanned barcode
				scannedId = query.getItemIdByBarcode(lastScanned);

				// get the item of the scanned barcode
				Item scannedItem = query.getItemById(scannedId);
				ItemType type = query.getItemTypeById(scannedItem.getItem_type_id());

				// prepare launch of ItemView
				i = new Intent(BarcodeLookupView.this, ItemView.class);

				Bundle b = new Bundle();
				b.putAll(getIntent().getExtras());
				b.putInt(Keys.KEY_ITEMID, scannedId);

				ArrayList<String> barcodeList = new ArrayList<String>();
				barcodeList.add(lastScanned);
				b.putStringArrayList(Keys.KEY_BARCODE, barcodeList);

				// check if item is a container
				if (type.getIs_container()) {
					b.putBoolean(Keys.KEY_ISCONTAINER, true);
				} else {
					b.putBoolean(Keys.KEY_ISCONTAINER, false);
				}

				i.putExtras(b);
				success = true;

				return null;
			} catch (NullPointerException e) {
				success = false;
				return null;
			} catch (ConnectTimeoutException e) {
				timeout = true;
				return null;
			}
		}

		@Override
		protected void onPostExecute(Void result) {
			progressDialog.dismiss();
			if (success) {
				setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
				startActivity(i);
			} else if (timeout) {
				Toast toast = Toast.makeText(
						BarcodeLookupView.this, 
						"" + lastScanned + " " + getString(R.string.error_timeout),
						Toast.LENGTH_SHORT);
				toast.show();
				manager.startScanning();
			} else {
				Toast toast = Toast.makeText(
						BarcodeLookupView.this, 
						"" + lastScanned + " " + getString(R.string.barcode_error_lookup),
						Toast.LENGTH_SHORT);
				toast.show();
				manager.startScanning();
			}
		}
	}
}